package com.ruoyi.system.service.aggregation.impl;

import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import com.ruoyi.common.config.OptimizationConfig;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.system.domain.aggregation.Product;
import com.ruoyi.system.mapper.aggregation.ProductMapper;
import com.ruoyi.system.service.aggregation.IProductStockService;

/**
 * 商品库存缓存Service实现
 * 
 * @author ruoyi
 * @date 2025-01-13
 */
@Service
public class ProductStockServiceImpl implements IProductStockService
{
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    @Autowired
    private ProductMapper productMapper;
    
    @Autowired
    private OptimizationConfig optimizationConfig;
    
    private static final String STOCK_KEY_PREFIX = "product:stock:";
    
    /**
     * 初始化商品库存到Redis
     */
    @Override
    public void initStock(Long productId, Integer stock)
    {
        if (!optimizationConfig.getStock().isCacheEnabled()) {
            return;
        }
        
        String key = STOCK_KEY_PREFIX + productId;
        redisTemplate.opsForValue().set(key, stock);
        
        System.out.println("✅ 商品库存已初始化到Redis: productId=" + productId + ", stock=" + stock);
    }
    
    /**
     * 扣减库存（原子操作）
     */
    @Override
    public boolean decrStock(Long productId, Integer quantity)
    {
        if (!optimizationConfig.getStock().isCacheEnabled()) {
            // 未启用缓存，直接操作数据库
            return decrStockInDb(productId, quantity);
        }
        
        String key = STOCK_KEY_PREFIX + productId;
        
        // 使用Lua脚本保证原子性
        String luaScript = 
            "local stock = redis.call('get', KEYS[1]) " +
            "if not stock then " +
            "    return -1 " +  // 库存未初始化
            "end " +
            "stock = tonumber(stock) " +
            "if stock < tonumber(ARGV[1]) then " +
            "    return 0 " +   // 库存不足
            "end " +
            "redis.call('decrby', KEYS[1], ARGV[1]) " +
            "return 1";         // 扣减成功
        
        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        script.setScriptText(luaScript);
        script.setResultType(Long.class);
        
        Long result = redisTemplate.execute(script, 
            Collections.singletonList(key), 
            quantity);
        
        if (result == null) {
            throw new ServiceException("库存扣减失败");
        }
        
        if (result == -1) {
            // 库存未初始化，从数据库加载
            loadStockFromDb(productId);
            // 重试一次
            result = redisTemplate.execute(script, 
                Collections.singletonList(key), 
                quantity);
        }
        
        if (result == 0) {
            throw new ServiceException("商品库存不足");
        }
        
        System.out.println("✅ 库存扣减成功: productId=" + productId + ", quantity=" + quantity);
        return true;
    }
    
    /**
     * 数据库扣减库存（降级方案）
     */
    private boolean decrStockInDb(Long productId, Integer quantity)
    {
        Product product = productMapper.selectProductById(productId);
        if (product == null) {
            throw new ServiceException("商品不存在");
        }
        
        if (product.getNum() < quantity) {
            throw new ServiceException("商品库存不足");
        }
        
        product.setNum(product.getNum() - quantity);
        productMapper.updateProduct(product);
        return true;
    }
    
    /**
     * 恢复库存
     */
    @Override
    public void incrStock(Long productId, Integer quantity)
    {
        if (!optimizationConfig.getStock().isCacheEnabled()) {
            // 未启用缓存，直接操作数据库
            incrStockInDb(productId, quantity);
            return;
        }
        
        String key = STOCK_KEY_PREFIX + productId;
        redisTemplate.opsForValue().increment(key, quantity);
        
        System.out.println("✅ 库存已恢复: productId=" + productId + ", quantity=" + quantity);
    }
    
    /**
     * 数据库恢复库存
     */
    private void incrStockInDb(Long productId, Integer quantity)
    {
        Product product = productMapper.selectProductById(productId);
        if (product == null) {
            throw new ServiceException("商品不存在");
        }
        
        product.setNum(product.getNum() + quantity);
        productMapper.updateProduct(product);
    }
    
    /**
     * 获取Redis中的库存
     */
    @Override
    public Integer getStock(Long productId)
    {
        if (!optimizationConfig.getStock().isCacheEnabled()) {
            // 未启用缓存，从数据库查询
            Product product = productMapper.selectProductById(productId);
            return product != null ? product.getNum() : 0;
        }
        
        String key = STOCK_KEY_PREFIX + productId;
        Object stock = redisTemplate.opsForValue().get(key);
        
        if (stock == null) {
            // 缓存未命中，从数据库加载
            loadStockFromDb(productId);
            stock = redisTemplate.opsForValue().get(key);
        }
        
        return stock != null ? Integer.parseInt(stock.toString()) : 0;
    }
    
    /**
     * 同步Redis库存到数据库
     */
    @Override
    public void syncStockToDb(Long productId)
    {
        if (!optimizationConfig.getStock().isCacheEnabled()) {
            return;
        }
        
        String key = STOCK_KEY_PREFIX + productId;
        Object stock = redisTemplate.opsForValue().get(key);
        
        if (stock != null) {
            Product product = productMapper.selectProductById(productId);
            if (product != null) {
                Integer currentStock = Integer.parseInt(stock.toString());
                product.setNum(currentStock);
                productMapper.updateProduct(product);
                
                System.out.println("✅ 库存已同步到数据库: productId=" + productId + ", stock=" + currentStock);
            }
        }
    }
    
    /**
     * 同步所有库存到数据库（定时任务调用）
     */
    @Override
    public void syncAllStockToDb()
    {
        if (!optimizationConfig.getStock().isCacheEnabled()) {
            return;
        }
        
        try {
            // 查询所有商品（不筛选状态）
            Product query = new Product();
            List<Product> products = productMapper.selectProductList(query);
            
            int syncCount = 0;
            for (Product product : products) {
                try {
                    syncStockToDb(product.getId());
                    syncCount++;
                } catch (Exception e) {
                    System.err.println("同步库存失败: productId=" + product.getId() + ", error=" + e.getMessage());
                }
            }
            
            System.out.println("✅ 批量同步库存完成，共同步 " + syncCount + " 个商品");
        } catch (Exception e) {
            System.err.println("批量同步库存失败: " + e.getMessage());
        }
    }
    
    /**
     * 初始化所有商品库存到Redis（系统启动时调用）
     */
    public void initAllStockToRedis()
    {
        if (!optimizationConfig.getStock().isCacheEnabled()) {
            return;
        }
        
        try {
            Product query = new Product();
            List<Product> products = productMapper.selectProductList(query);
            
            int initCount = 0;
            for (Product product : products) {
                try {
                    if (product.getNum() != null && product.getNum() > 0) {
                        initStock(product.getId(), product.getNum());
                        initCount++;
                    }
                } catch (Exception e) {
                    System.err.println("初始化库存失败: productId=" + product.getId() + ", error=" + e.getMessage());
                }
            }
            
            System.out.println("✅ 系统启动：批量初始化库存到Redis完成，共初始化 " + initCount + " 个商品");
        } catch (Exception e) {
            System.err.println("批量初始化库存失败: " + e.getMessage());
        }
    }
    
    /**
     * 从数据库加载库存到Redis
     */
    @Override
    public void loadStockFromDb(Long productId)
    {
        if (!optimizationConfig.getStock().isCacheEnabled()) {
            return;
        }
        
        Product product = productMapper.selectProductById(productId);
        if (product != null) {
            initStock(productId, product.getNum());
        }
    }
}

